#include <fstream>
#include <iostream>
#include "hashChains.h"
#include <cstring>

const int DIVISOR = 4099;
const int MAX_CODES = 4096;
const int BYTE_SIZE = 8;
const int EXCESS = 4; 
const int ALPHA = 256;
const int MASK1 = 0xFF;//255;
const int MASK2 = 0x0F;//15;


using namespace std;

typedef pair<const long, int> pairType;

int leftOver;
bool bitsLeftOver = false;
ifstream in;
ofstream out;

void output(long code);

void setFiles(int argc, char *argv)
{
    char outFile[64], inFile[64];

    memset(inFile, 0, sizeof(inFile));

    if(argc < 2) {
        cout << "Enter name of file to compress" << endl;
        cin >> inFile;
    } else {
        strncpy(inFile, argv, sizeof(inFile));
    }

    in.open(inFile, ios::binary);
    if(in.fail()) {
        cerr << "Cannot open " << inFile << endl;
        exit(1);
    }

    strncpy(outFile, inFile, sizeof(outFile));
    strncat(outFile, ".zzz", strlen(".zzz"));

    out.open(outFile, ios::binary);

    if(out.fail()) {
        cerr << "Cannot open output file " << outFile << endl;
        in.close();
        exit(1);
    }


}

void compress(void)
{
    hashChains<long, int> h(DIVISOR);
    int codeUsed = 0;
    int c;

    for(int i = 0; i < ALPHA; i++) 
        h.insert(pairType(i,i));
   
    codeUsed = ALPHA;

    c = in.get();
    if(c != EOF) {
        long prefixCode = c;
        while((c = in.get()) != EOF) {
            long theKey = (prefixCode  << BYTE_SIZE) + c;
            pairType* thePair = h.find(theKey);
            if(thePair == NULL) {
                output(prefixCode);
                if(codeUsed < MAX_CODES) 
                    h.insert(pairType(theKey, codeUsed++));

                prefixCode = c;
            } else {
                prefixCode = thePair->second;
            }
        }

        output(prefixCode);
        if(bitsLeftOver) out.put(leftOver << EXCESS);
    }

    out.close();
    in.close();
}

/*
 * pcode --> prefix code
 */
void output(long pcode)
{
    int c, d;
    if(bitsLeftOver) {
        d = int(pcode & MASK1);
        c = int((leftOver << EXCESS) | (pcode >> BYTE_SIZE));
        out.put(c);
        out.put(d);
        bitsLeftOver = false;
    } else {
        leftOver = pcode & MASK2;
        c = int(pcode >> EXCESS);
        out.put(c);
        bitsLeftOver = true;
    }

}

void testCompress(void)
{
   setFiles(3, (char *)"abc.txt");
   compress();
}
